/*
 * @(#)ApplicationContainer.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.framework;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dcivision.dms.bean.DmsDocument;
import com.dcivision.framework.bean.AuditLoginOut;
import com.dcivision.framework.bean.SysFunction;

/**
  ApplicationContainer.java

  This class is stored in the application scope and provide all system level
  functions.

    @author          Rollo Chan
    @company         DCIVision Limited
    @creation date   25/06/2003
    @version         $Revision: 1.32 $
*/

public class ApplicationContainer {

  public static final String REVISION = "$Revision: 1.32 $";

  protected Log log = LogFactory.getLog(this.getClass().getName());
  protected Hashtable hashLoginUser = null;
  protected Hashtable hashUsingFunction = null;
  protected Hashtable hashSystemUser = null;
  protected Hashtable hashSysFunction = null;
  protected Hashtable hashSysFunctionCodeToID = null;
  protected Hashtable hashDmsDocumentOperationID = null;
  protected Hashtable hashDuplicateNameCache = null;
  protected List listSysFunction = null;
  protected String versionStr = "";

  public ApplicationContainer() {
    this.hashLoginUser = new Hashtable();
    this.hashUsingFunction = new Hashtable();
    this.hashDmsDocumentOperationID = new Hashtable();
    this.hashDuplicateNameCache = new Hashtable();
  }

  /**
   * loginUser
   *
   * Log the user who is login to the system.
   *
   * @param userRecordID
   */
  public void loginUser(AuditLoginOut auditRecord) {
    hashLoginUser.put(auditRecord.getUserRecordID(), auditRecord);
  }

  /**
   * logoutUser
   *
   * Log the user who is logout from the system.
   *
   * @param userRecordID
   */
  public void logoutUser(Integer userRecordID) {
    if (userRecordID==null) {
      return;
    }
    hashLoginUser.remove(userRecordID);
    hashUsingFunction.remove(userRecordID);
  }

  /**
   * updateUsingFunction
   *
   * Log the user currently using which function.
   *
   * @param userRecordID
   * @param functionCode
   */
  public void updateUsingFunction(Integer userRecordID, String functionCode) {
    if (userRecordID==null) {
      return;
    }
    if (Utility.isEmpty(functionCode)) {
      this.hashUsingFunction.remove(userRecordID);
    } else {
      this.hashUsingFunction.put(userRecordID, functionCode);
    }
  }

  /**
   * getUsingFunction
   *
   * @param userRecordID  The user record ID.
   * @return              The system function which the user is using.
   */
  public SysFunction getUsingFunction(Integer userRecordID) {
    String functionCode = (String)this.hashUsingFunction.get(userRecordID);
    if (functionCode==null) {
      return(null);
    } else {
      return (this.getSysFunctionByFunctionCode(functionCode));
    }
  }

  /**
   * isOnline
   *
   * Check whether the user is already login or not.
   *
   * @param userRecordID
   * @param sessionID
   * @return
   */
  public boolean isOnline(Integer userRecordID, String sessionID) {
    AuditLoginOut auditRecord = (AuditLoginOut)hashLoginUser.get(userRecordID);
    if (auditRecord!=null && sessionID!=null) {
      return(sessionID.equals(auditRecord.getSessionID()));
    }
    return(false);
  }

  /**
   * isLogin
   *
   * Check whether the user is already login or not.
   *
   * @param userRecordID
   * @return
   */
  public boolean isLogin(Integer userRecordID) {
    return(hashLoginUser.get(userRecordID)!=null);
  }

  /**
   * getCurrentLoginUserList
   *
   * @return A list of current login user in the format of ArrayList of
   *         AuditLoginOut objects.
   */
  public List getCurrentLoginUserList() {
    ArrayList result = new ArrayList(this.hashLoginUser.values());
    for (int i=0; i<result.size();i++) {
      AuditLoginOut auditRecord = (AuditLoginOut)result.get(i);
      auditRecord.setRecordCount(result.size());
      auditRecord.setRowNum(i+1);
    }
    return(result);
  }

  /**
   * getSysFunctionList
   *
   * @return A list of SysFunction in the system.
   */
  public List getSysFunctionList() {
    return (this.listSysFunction);
  }

  /**
   * setSysFunctionList
   *
   * @param sysFunction  A list of SysFunction in the system.
   */
  public void setSysFunctionList(List sysFunction) {
     this.hashSysFunction = new Hashtable();
     this.hashSysFunctionCodeToID = new Hashtable();
     for (int i = 0; i < sysFunction.size(); i++) {
       this.hashSysFunction.put(((SysFunction)sysFunction.get(i)).getID(), sysFunction.get(i));
       this.hashSysFunctionCodeToID.put(((SysFunction)sysFunction.get(i)).getFunctionCode(), ((SysFunction)sysFunction.get(i)).getID());
     }
     this.listSysFunction = sysFunction;
  }

  /**
   * getSysFunctionByFunctionID
   *
   * @param nFunctionID  The function ID.
   * @return             The SysFunction object of that function ID.
   */
  public SysFunction getSysFunctionByFunctionID(Integer nFunctionID) {
    if (nFunctionID == null) {
      return(null);
    } else {
      return ((SysFunction)this.hashSysFunction.get(nFunctionID));
    }
  }

  /**
   * getSysFunctionByFunctionCode
   *
   * @param sFunctionCode  The function code.
   * @return               The SysFunction object of that function code.
   */
  public SysFunction getSysFunctionByFunctionCode(String sFunctionCode) {
    if (sFunctionCode == null) {
      return(null);
    } else {
      Integer nFunctionID = (Integer)this.hashSysFunctionCodeToID.get(sFunctionCode);
      if (nFunctionID == null) {
        return(null);
      } else {
        return(getSysFunctionByFunctionID(nFunctionID));
      }
    }
  }

  public void setVersion(String version) {
    this.versionStr = version;
  }

  public boolean isLightVersion() {
    return(this.versionStr.startsWith("L"));
  }

  public boolean isEnterpriseVersion() {
    return(this.versionStr.startsWith("E"));
  }

  public boolean isDemoVersion() {
    return(this.versionStr.startsWith("D"));
  }

  /**
   * getCurrentUsingFunction
   *
   * @param userRecordID  The user record ID.
   * @return              The SysFunction object of that user is currently using.
   */
  public SysFunction getCurrentUsingFunction(Integer userRecordID) {
    /************************************************************
     * TODO: Get the current function which the user is using.
     ************************************************************/
    return(null);
  }
    
  public synchronized void lockDocumentOperationID(String sessionID, List listOperationID) {
    this.hashDmsDocumentOperationID.put(sessionID, listOperationID);
  }
  
  /**
   * Check the operation id by action type. 
   * rejectActions make with char 'C'(copy) 'D'(delete) 'M'(move) 'U'(update) 'I'(index)
   * For example: 
   *   rejectActions='DMI', indicate that only 'Hashtable' key start with 'D' or 'M' or 'I' would be checked.
   * lee add in 06/06/26
   * @param sessionID
   * @param listOperationID
   * @param rejectActions
   * @return
   */
  public synchronized boolean checkAndLockDocumentOperationID(String sessionID, List listOperationID, String rejectActions) {
    Enumeration enumeration = this.hashDmsDocumentOperationID.keys();
    while(enumeration.hasMoreElements()) {
      String key = (String)enumeration.nextElement();
      if (rejectActions.indexOf(key.substring(0,1))!=-1) {
        List listID = (List)this.hashDmsDocumentOperationID.get(key);
        if (listID!=null && !listID.isEmpty()) {
          for (int i=0; i<listID.size(); i++) {
            if (listOperationID.contains(listID.get(i))) {
              return false;
            }
          }
        }
      }
    }
    this.hashDmsDocumentOperationID.put(sessionID, listOperationID);
    return true;
  }
  
  public synchronized void unlockDmsDocumentOperationID(String sessionID) {
    this.hashDmsDocumentOperationID.remove(sessionID);
  }
  
  /**
   * Regedit user thread to duplicate name cache
   * EIP-438/EIP-539 06/12/27 LEE
   */
  public void regeditDuplicateNameCache() {
    if (!this.hashDuplicateNameCache.containsKey(Thread.currentThread())) {
      synchronized (this.hashDuplicateNameCache) {
        if (!this.hashDuplicateNameCache.containsKey(Thread.currentThread())) {
          this.hashDuplicateNameCache.put(Thread.currentThread(), new ArrayList());
        }
      }
    }
  }
  
  /**
   * Check dms duplicate name from operation cache. 
   * EIP-438/EIP-539 06/12/27 LEE
   * @param cacheKey
   * @param parentID
   * @param documentName
   * @param documentType
   * @return
   */
  public boolean checkAndLockDuplicateNameCache(Integer parentID, String documentName, String documentType) {
    String cache = DmsDocument.DOCUMENT_LINK.equals(documentType) ? (parentID+"::"+DmsDocument.DOCUMENT_LINK+"::"+documentName) : (parentID+"::"+documentName);
    return checkAndLockDuplicateNameCache(cache);
  }
  
  /**
   * Check dms duplicate name from operation cache.
   * EIP-438 06/12/27 LEE
   * @param cacheKey
   * @param cache
   * @return
   */  
  public boolean checkAndLockDuplicateNameCache(String cache) {
    Thread currentThread = Thread.currentThread();
    if (this.hashDuplicateNameCache.containsKey(currentThread)) {
      // has regedit current user thread, duplicate-name check would be start
      
      // step 1: check duplicate name
      Iterator hashCaches = this.hashDuplicateNameCache.values().iterator();
      boolean found = false;
      while (hashCaches.hasNext()) {
        List listCaches = (List)hashCaches.next();
        if (listCaches.contains(cache)) {
          found = true;
          break;
        }
      }
      
      // step 2: lock duplicate name and return check result.
      if (!found) {
        List listCaches = (List)this.hashDuplicateNameCache.get(currentThread);
        synchronized (this.hashDuplicateNameCache) {
          listCaches.add(cache);
          this.hashDuplicateNameCache.put(currentThread, listCaches);
        }
        return true;
      } else {
        return false;
      }
    }else {
      // has not regedit current user thread, duplicate name check would be ignore and disable.
      return true;
    }
  }
  
  /**
   * Remove dms duplicate name cache from application
   * @param cacheKey
   */
  public void unlockDuplicateNameCache() {
    synchronized (this.hashDuplicateNameCache) {
      this.hashDuplicateNameCache.remove(Thread.currentThread());
    }
  }
}